package annotation

import (
	"errors"
	"fmt"
	pgdb "gsc-datasets/internal/db/sqlc"
	"gsc-datasets/internal/rest/constants"
	appMiddleware "gsc-datasets/internal/rest/middlewares"
	appRender "gsc-datasets/internal/rest/render"
	datasetEntityRender "gsc-datasets/internal/rest/render/entity/dataset"
	"gsc-datasets/internal/rest/routers"
	"gsc-datasets/pkg/util"
	"net/http"
	"strconv"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/render"
)

type DatasetAnnotationRouter struct {
	*routers.RestRouter
}

// New initialization
func New(
	rr *routers.RestRouter,
) *DatasetAnnotationRouter {
	s := &DatasetAnnotationRouter{
		rr,
	}
	s.register()
	return s
}

func (s *DatasetAnnotationRouter) register() {
	r := s.ChiRouter()
	r.Use(appMiddleware.WithUser(s.RestRouter, constants.UserKey), appMiddleware.WithDataset(s.RestRouter, constants.DatasetKey))
	r.With(appMiddleware.WithPager(s.RestRouter)).Get("/", s.listDatasetAnnotations)
	r.Post("/", s.createDatasetAnnotation)
	r.Route(fmt.Sprintf("/{%s}", constants.DatasetAnnotationKey), func(r chi.Router) {
		r.Use(appMiddleware.WithDatasetAnnotation(s.RestRouter, constants.DatasetAnnotationKey))
		r.Get("/", s.getDatasetAnnotation)
		r.Put("/", s.updateDatasetAnnotation)
		r.Delete("/", s.deleteDatasetAnnotation)
	})
}

// 获取数据标注列表(单数据项)
func (s *DatasetAnnotationRouter) listDatasetAnnotations(w http.ResponseWriter, r *http.Request) {
	logger := s.Logger("DatasetAnnotationRouter")
	logger.Info("-listDatasetAnnotations-")
	ctx := r.Context()
	datasetEntryIDVal := r.URL.Query().Get("dataset_entry_id")
	if util.IsEmpty(datasetEntryIDVal) {
		render.Render(w, r, appRender.ErrBadRequest(fmt.Errorf("dataset_entry_id is empty")))
		return
	}
	datasetEntryID, err := strconv.ParseInt(datasetEntryIDVal, 10, 64)
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	datasetPtr := ctx.Value(constants.DatasetKey).(*pgdb.Dataset)
	userPtr := ctx.Value(constants.UserKey).(*pgdb.User)
	pagerPtr := ctx.Value(util.PagerKey{}).(*util.Pager)

	logger.Infof("dataset_id:%d,datasetEntryID:%d", datasetPtr.ID, datasetEntryID)
	logger.Infof("pager:%+v", pagerPtr)

	total, err := s.Dao().CountDatasetAnnotationsByEntryAndUser(ctx, pgdb.CountDatasetAnnotationsByEntryAndUserParams{
		Status:         1,
		DatasetID:      datasetPtr.ID,
		DatasetEntryID: datasetEntryID,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	exDatasetAnnotations, err := s.Dao().ListDatasetAnnotationsByEntryAndUser(ctx, pgdb.ListDatasetAnnotationsByEntryAndUserParams{
		Status:         1,
		DatasetID:      datasetPtr.ID,
		DatasetEntryID: datasetEntryID,
		UserID:         userPtr.ID,
		Offset:         pagerPtr.Offset,
		Limit:          pagerPtr.Limit,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	logger.Infof("exDatasetAnnotations:%d", len(exDatasetAnnotations))
	if err := render.Render(w, r, appRender.EntityPagedListToRender(total, exDatasetAnnotations)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 创建数据集标注
func (s *DatasetAnnotationRouter) createDatasetAnnotation(w http.ResponseWriter, r *http.Request) {
	logger := s.Logger("DatasetAnnotationRouter")
	logger.Info("-createDatasetAnnotation-")
	data := &datasetEntityRender.CreateDatasetAnnotataionRequest{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrBadRequest(err))
		return
	}
	ctx := r.Context()
	datasetPtr := ctx.Value(constants.DatasetKey).(*pgdb.Dataset)
	userPtr := ctx.Value(constants.UserKey).(*pgdb.User)
	var datasetAnnotationPtr *pgdb.DatasetAnnotation
	err := s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
		data.CreateDatasetAnnotationParams.DatasetID = datasetPtr.ID
		datasetAnnotation, err := q.CreateDatasetAnnotation(ctx, *data.CreateDatasetAnnotationParams)
		if err != nil {
			return
		}
		err = q.UpsertDatasetUserAnnotation(ctx, pgdb.UpsertDatasetUserAnnotationParams{
			UserID:       userPtr.ID,
			AnnotationID: datasetAnnotation.ID,
		})
		if err != nil {
			return
		}
		datasetAnnotationPtr = &datasetAnnotation
		return
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.EntityToRender(datasetAnnotationPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 获取数据集标注
func (s *DatasetAnnotationRouter) getDatasetAnnotation(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	datasetAnnotationPtr := ctx.Value(constants.DatasetAnnotationKey).(*pgdb.DatasetAnnotation)
	if err := render.Render(w, r, appRender.EntityToRender(datasetAnnotationPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 修改数据集标注
func (s *DatasetAnnotationRouter) updateDatasetAnnotation(w http.ResponseWriter, r *http.Request) {
	logger := s.Logger("DatasetAnnotationRouter")
	logger.Info("-updateDatasetAnnotation-")
	data := &datasetEntityRender.UpdateDatasetAnnotationRequest{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrBadRequest(err))
		return
	}
	ctx := r.Context()
	datasetAnnotationPtr := ctx.Value(constants.DatasetAnnotationKey).(*pgdb.DatasetAnnotation)
	data.UpdateDatasetAnnotationToLabelParams.ID = datasetAnnotationPtr.ID
	err := s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
		datasetAnnotation, err := q.UpdateDatasetAnnotationToLabel(ctx, *data.UpdateDatasetAnnotationToLabelParams)
		if err != nil {
			return
		}
		datasetAnnotationPtr = &datasetAnnotation
		return
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.EntityToRender(datasetAnnotationPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 删除数据集标注
func (s *DatasetAnnotationRouter) deleteDatasetAnnotation(w http.ResponseWriter, r *http.Request) {
	logger := s.Logger("DatasetAnnotationRouter")
	logger.Info("-deleteDatasetAnnotation-")
	ctx := r.Context()
	datasetPtr := ctx.Value(constants.DatasetKey).(*pgdb.Dataset)
	userPtr := ctx.Value(constants.UserKey).(*pgdb.User)
	datasetAnnotationPtr := ctx.Value(constants.DatasetAnnotationKey).(*pgdb.DatasetAnnotation)
	if datasetAnnotationPtr.DatasetID != datasetPtr.ID {
		render.Render(w, r, appRender.ErrRender(errors.New(("数据不一致"))))
		return
	}
	exist, err := s.Dao().CheckExistDatasetUserAnnotataion(ctx, pgdb.CheckExistDatasetUserAnnotataionParams{
		UserID:       userPtr.ID,
		AnnotationID: datasetAnnotationPtr.ID,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if !exist {
		render.Render(w, r, appRender.ErrRender(errors.New(("无权删除该节点"))))
		return
	}
	err = s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
		err = q.DeleteDatasetUserAnnotation(ctx, pgdb.DeleteDatasetUserAnnotationParams{
			UserID:       userPtr.ID,
			AnnotationID: datasetAnnotationPtr.ID,
		})
		if err != nil {
			return
		}
		deletable, err := q.CheckDatasetAnnotationDeletable(ctx, datasetAnnotationPtr.ID)
		if err != nil {
			return
		}
		logger.Infof("-deleteDatasetAnnotation-deletable:%+v", deletable)
		if !deletable {
			err = fmt.Errorf("delete is forbid for fk constraint")
			return
		}
		deletedDatasetAnnotation, err := q.DeleteDatasetAnnotation(ctx, datasetAnnotationPtr.ID)
		if err != nil {
			return
		}
		datasetAnnotationPtr = &deletedDatasetAnnotation
		return
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.EntityToRender(datasetAnnotationPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}
